iT邦幫忙

2022 iThome 鐵人賽

DAY 23
1
Software Development

軟體架構師的自我修養系列 第 23

[Day 23] MongoDB索引的奧秘

  • 分享至 

  • xImage
  •  

我相信我們都很熟悉MySQL的索引規則了,因為MySQL的儲存引擎是用B+樹的資料結構儲存索引,所以會有一個最左匹配的規則。

為了讓查詢能夠吻合索引,索引使用欄位的順序必須由左至右,舉例來說:

SELECT * FROM table WHERE a = 1 AND b > 2 ORDER BY c;

這查詢最有效率的索引是一個複合索引,例如(a, b, c)。但是,這樣的索引無法完全套用在下面這個查詢。

SELECT * FROM table WHERE a = 1 ORDER BY c;

這是因為,查詢少了b這個欄位,因此這個複合索引只有第一個a欄位會被使用。

這是MySQL的索引規則,大體來說,關聯式資料庫都遵守這樣的規則。但是,MongoDB的實作有點不同,儘管MongoDB的索引是以B樹儲存。

MongoDB的ESR規則

我們繼續沿用剛剛的查詢作為例子,以下這個查詢與剛剛MySQL的查詢是等價的,只是用MongoDB的MQL改寫了。

db.table.find({a: 1, b: {$gt: 2}}).sort({c: 1})

但是剛剛的索引(a, b, c)對於這個查詢是無效的,取而代之的是,必須要用(a, c, b)才能提升效能。

這是因為MongoDB的索引遵循ESR規則,也就是Equality-Sort-Range。因此,剛剛的查詢,b欄位是範圍查詢R,而c欄位是排序S。

所以正確的索引順序是(a, c, b)

MongoDB索引交集(Index Intersection)

與一般的關聯式資料庫相同,MongoDB亦支援索引交集。

索引交集指的是單一一個查詢,可以套用複數個索引,會使用這些索引的交集條件。

繼續以上面的查詢作為例子。

db.table.find({a: 1, b: {$gt: 2}}).sort({c: 1})

為了提升效能,我們剛提到要使用(a, c, b)這個索引,但事實上,也可以透過兩個單獨索引來提升效能:

  1. b
  2. (a, c)

透過這兩個索引的交集,一樣可以提升查詢的效能,值得注意的是,ESR規則依然有效。

雖然官方文件說有支援索引交集,但我並不推薦大家主動使用,因為MongoDB的索引選擇器不一定足夠聰明去選擇兩個索引,另一方面,即便可以交集,但依然會需要CPU的運算,會比單一索引更消耗資源。

那索引交集的優點是什麼?

最大的優點是我們可以讓索引更彈性。如果只有一個索引(a, c, b),那麼在單獨查詢b時就無法套用索引,因此得要再額外建立一個b欄位的索引。

越多索引就會佔用越多記憶體,同時也會影響資料寫入的效能,所以索引交集可以讓索引的符合條件比較緊密。

但如我剛說的,不要把索引交集當成一個目的,而是當成一種背後的補救機制,當索引漏掉時「有機會」因為索引交集得到補救,但這不是一個可靠的機制。

MongoDB的ESR隱藏規則

當使用MySQL時,我們會用IN做範圍的查詢,在MySQL中IN是一種相等比對,也就是說,當查詢是WHERE a IN (2, 3)等價於WHERE a = 2 OR a = 3

但是,MongoDB不這麼認為。

如果在查詢時單獨使用$in,那麼就可以當作與MySQL相同行為,屬於ESR中的E。

舉例來說:
find({a: {$in: [2, 3]}})find({$or: [{a: 2}, {a: 3}]})是等價的,無論是前者還後者,都屬於E。

但是,如果和排序一起使用,那麼$in就要視為範圍比對,也就是R。

find({a: {$in: [2, 3]}}).sort({b: 1})

這裡的a會被視為R,所以為了吻合對應的查詢,索引要使用(b, a)而不是(a, b)

結論

如果只有使用關聯式資料庫的經驗,那在使用功能更豐富的NoSQL資料庫很容易踩到陷阱。尤其是MongoDB的底層是B樹其實與關聯式資料庫差不多,但是在實作上還是有許多差別。

當在建立MongoDB的索引時,要特別注意ESR規則。許多從MySQL轉移過來的MongoDB使用者非常容易在這個規則上栽跟斗。

事實上,即便是索引(a, b, c),在資料量體小的時候還是可以運作,MongoDB會將資料拉出來後在記憶體中進行排序。一但資料量大到無法裝進記憶體,而必須得要用到硬碟存取,那麼效能非常悲劇。


上一篇
[Day 22] MongoDB的片鍵該如何選擇?
下一篇
[Day 24] Redis的資料持久化
系列文
軟體架構師的自我修養31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言